package com.xcjh.base;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xcjh.base.dto.Constants;
import com.xcjh.base.dto.Result;
import com.xcjh.base.dto.Status;
import com.xcjh.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 *  杨懿
 * @param <S> Service类名
 * @param <E> Entity实体类名
 */
@RestController
public abstract class BaseController<S extends IService<E>, E > {

    private static final String HTTP_X_FORWARDED_FOR ="HTTP_X_FORWARDED_FOR";
    private static final String HTTP_HEADER_UNKNOWN ="HTTP_HEADER_UNKNOWN" ;
    private static final String COMMA = ",";
    private static final String HTTP_X_REAL_IP = "HTTP_X_REAL_IP";

    private static final String DATE_FORMATE_TIME ="yyyy-MM-dd HH:mm:ss";
    private static final String DATE_FORMATE_DATE ="yyyy-MM-dd";

    @Autowired
    protected RedisUtils redisUtils;

    @Autowired
    private PermissionServiceClient permissionServiceClient;


    /**
     * 获取当前用户的角色
     *
     * @return
     */
//    public List<PermissionRoleDto> getRoles() {
//        long userId = this.getUserIdByToken();
//        DoctorHospitalDto doctorHospitalDto = (DoctorHospitalDto) doctorServiceClient.getHospitalInfoById(userId).getData();
//        if (doctorHospitalDto == null || doctorHospitalDto.getHospitalId() == null) {
//            throw new PicaException("40000", "权限被拒绝");
//        }
//        List<PermissionRoleDto> roles = (List<PermissionRoleDto>) permissionServiceClient.getPermissionRoles(userId).getData();
//        if (!CollectionUtils.isEmpty(roles)) {
//            return roles;
//        }
//        return null;
//    }

    /**
     * 从请求头中获取token
     *
     * @return
     */
    public String getTokenFromHeader() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getRequest().getHeader("token");
    }

    /**
     * get ip address in the http request
     *
     * @param request http servlet request
     * @return client ip address
     */
    public static String getClientIpAddress(HttpServletRequest request) {
        String clientIp = request.getHeader(HTTP_X_FORWARDED_FOR);

        if (!StringUtils.isEmpty(clientIp) && !clientIp.equalsIgnoreCase(HTTP_HEADER_UNKNOWN)) {

            int index = clientIp.indexOf(COMMA);
            if (index != -1) {
                return clientIp.substring(0, index);
            } else {
                return clientIp;
            }
        }

        clientIp = request.getHeader(HTTP_X_REAL_IP);
        if (!StringUtils.isEmpty(clientIp) && !clientIp.equalsIgnoreCase(HTTP_HEADER_UNKNOWN)) {
            return clientIp;
        }

        return request.getRemoteAddr();
    }

    /**
     * 获取客户端ip
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.equals("0:0:0:0:0:0:0:1")) {
            try {
                ip = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
        return ip;
    }
    /**
     * return data list
     *
     * @param result result code
     * @return result code
     */
    protected Result returnDataList(Map<String, Object> result) {
        Status status = (Status) result.get(Constants.STATUS);
        if (status == Status.SUCCESS) {
            String msg = Status.SUCCESS.getMsg();
            Object datalist = result.get(Constants.DATA_LIST);
            return success(msg, datalist);
        } else {
            Integer code = status.getCode();
            String msg = (String) result.get(Constants.MSG);
            return error(code, msg);
        }
    }

    /**
     * success
     *
     * @return success result code
     */
    protected Result success() {
        Result result = new Result();
        result.setCode(Status.SUCCESS.getCode());
        result.setMassage(Status.SUCCESS.getMsg());

        return result;
    }

    /**
     * success does not need to return data
     *
     * @param msg success message
     * @return success result code
     */
    protected Result success(String msg) {
        Result result = new Result();
        result.setCode(Status.SUCCESS.getCode());
        result.setMassage(msg);

        return result;
    }

    /**
     * return data no paging
     *
     * @param msg success message
     * @param list data list
     * @return success result code
     */
    protected Result success(String msg, Object list) {
        return getResult(msg, list);
    }

    /**
     * return data no paging
     *
     * @param list success
     * @return success result code
     */
    protected Result success(Object list) {
        return getResult(Status.SUCCESS.getMsg(), list);
    }

    /**
     * return the data use Map format, for example, passing the value of key, value, passing a value
     * eg. "/user/add"  then return user name: zhangsan
     *
     * @param msg message
     * @param object success object data
     * @return success result code
     */
    protected Result success(String msg, Map<String, Object> object) {
        return getResult(msg, object);
    }

    /**
     * return data with paging
     *
     * @param totalList success object list
     * @param currentPage current page
     * @param total total
     * @param totalPage  total page
     * @return success result code
     */
    protected Result success(Object totalList, Integer currentPage,
                          Integer total, Integer totalPage) {
        Result result = new Result();
        result.setCode(Status.SUCCESS.getCode());
        result.setMassage(Status.SUCCESS.getMsg());

        Map<String, Object> map = new HashMap<>(4);
        map.put(Constants.TOTAL_LIST, totalList);
        map.put(Constants.CURRENT_PAGE, currentPage);
        map.put(Constants.TOTAL_PAGE, totalPage);
        map.put(Constants.TOTAL, total);
        result.setData(map);
        return result;
    }

    /**
     * error handle
     *
     * @param code result code
     * @param msg result message
     * @return error result code
     */
    protected Result error(Integer code, String msg) {
        Result result = new Result();
        result.setCode(code);
        result.setMassage(msg);
        return result;
    }

    /**
     * put message to map
     *
     * @param result result
     * @param status status
     * @param statusParams object messages
     */
    protected void putMsg(Map<String, Object> result, Status status, Object... statusParams) {
        result.put(Constants.STATUS, status);
        if (statusParams != null && statusParams.length > 0) {
            result.put(Constants.MSG, MessageFormat.format(status.getMsg(), statusParams));
        } else {
            result.put(Constants.MSG, status.getMsg());
        }
    }

    /**
     * put message to result object
     *
     * @param result result
     * @param status status
     * @param statusParams status parameters
     */
    protected void putMsg(Result result, Status status, Object... statusParams) {
        result.setCode(status.getCode());

        if (statusParams != null && statusParams.length > 0) {
            result.setMassage(MessageFormat.format(status.getMsg(), statusParams));
        } else {
            result.setMassage(status.getMsg());
        }

    }

    /**
     * get result
     * @param msg message
     * @param list object list
     * @return result code
     */
    private Result getResult(String msg, Object list) {
        Result result = new Result();
        result.setCode(Status.SUCCESS.getCode());
        result.setMassage(msg);

        result.setData(list);
        return result;
    }
    private Result getResult(Object list) {
        Result result = new Result();
        result.setCode(Status.SUCCESS.getCode());
        result.setMassage(Status.SUCCESS.getMsg());

        result.setData(list);
        return result;
    }


    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(
                new SimpleDateFormat(DATE_FORMATE_TIME), true));
        binder.registerCustomEditor(Date.class, new CustomDateEditor(
                new SimpleDateFormat(DATE_FORMATE_DATE), true));
    }



//    protected S baseService;
    protected abstract S getBaseService();//不适用AutoWaire是因为idea判断是否被bean注入,泛型不支持


    /**
     * 如果返回有值,则代表错误信息. 校验成功 通过则返回null.  子类自己重写实现校验
     */
    protected String validateSave(E e){
        return null;//子类自己实现.
    }
    protected String validateUpdate(E e){
        return null;//子类自己实现.
    }
    protected String validateDelete(Serializable e){
        return null;//子类自己实现.
    }

    @PostMapping
    public Result save(@RequestBody E e) {
        String status=validateSave(e);
        if(StringUtils.isEmpty(status)){
            getBaseService().save(e);
            return getResult(e);
        }else{
            return error(Status.ERROR.getCode(),status);
        }
    }

    @GetMapping("{id}")
    public Result detail(@PathVariable("id") Serializable id) {
        return getResult(getBaseService().getById(id));
    }

    @DeleteMapping("{id}")
    public Result delete(@PathVariable("id") Serializable id) {
        String status=validateDelete(id);
        if(StringUtils.isEmpty(status)){
            return getResult(getBaseService().removeById(id));
        }else{
            return error(Status.ERROR.getCode(),status);
        }
    }



    @PutMapping
    public Result update(@RequestBody E e) {
        String status=validateUpdate(e);
        if(StringUtils.isEmpty(status)){
            return getResult(getBaseService().saveOrUpdate(e));
        }else{
            return error(Status.ERROR.getCode(),status);
        }
    }


    /**
     * 不带业务参数的分页查询(一般不用)
     * @param page
     * @return
     */
    @GetMapping
    public Result list(Page<E> page) {
        return getResult(getBaseService().page(page));
    }

    /**
     * 带业务参数的分页查询.  例如查询:code=张三  (常用分页)
     * @param currentPage
     * @param size
     * @param entity
     * @return
     */
    @PostMapping("list/{currentPage}/{size}")
    public Result list(@PathVariable("currentPage") int currentPage, @PathVariable("size") int size,@RequestBody E entity) {
        QueryWrapper<E> wrapper = new QueryWrapper<E>(entity);
        return getResult(getBaseService().page(new Page<>(currentPage, size),wrapper));
    }

}
